type State = 'pending' | 'resolve' | 'reject'
type CB = undefined | Function

export default class Promise2 {

  state: State = 'pending'
  queue: [CB, CB, Promise2][] = []

  constructor(callback: Function) {
    callback.call(undefined, this.resolve.bind(this), this.reject.bind(this))
  }

  resolveOrReject(state: State, data: any){
    if(this.state !== 'pending') return
    this.state = state
    setTimeout(() => {
      const i = state === 'resolve' ? 0 : 1
      this.queue.forEach(item => {
        if(item[i]) {
          const x = item[i].call(undefined, data)
          this.resolveX(x, item[2])
        }else{
          if(state === 'resolve') item[2].resolve(data)
          if(state === 'reject') item[2].reject(data)
        }
      })
    })
  }

  resolveX(x, p: Promise2) {
    if(x instanceof Promise2) {
      x.then(
        res => p.resolve(res),
        err => p.reject(err)
      )
    }else {
      p.resolve(x)
    }
  }

  resolve(data?: any){
    this.resolveOrReject('resolve', data)
  }

  reject(data?: any){
    this.resolveOrReject('reject', data)
  }

  then(onresolve?: Function, onreject?: Function) {
    const p = new Promise2(() => {})
    this.queue.push([onresolve, onreject, p])
    return p
  }

  catch(onreject?: Function){
    return this.then(undefined, onreject)
  }

  static resolve(data?: any) {
    return new Promise2(s => s(data))
  }

  static reject(data?: any) {
    return new Promise2((s,j) => j(data))
  }

  static race(arr: Promise2[]) {
    return new Promise2((s, j) => {
      arr.forEach(p => {
        p.then(res => s(res))
        .catch(err => j(err))
      })
    })
  }

  static all(arr: Promise2[]) {
    const resArr = []
    return new Promise2((s, j) => {
      arr.forEach(p => {
        p.then(res => {
          resArr.push(res)
          if(resArr.length === arr.length) s(resArr)
        })
        .catch(err => j(err))
      })
    })
  }

}